; (function () {

  $.fn.vueTransform = function (config = {}) {
    const defaultConfig = {
      data: [],
      labelProp: 'label',
      valueProp: 'guid',
      leftItemHtml: null,
      rightItemHtml: null,
      leftHeaderHtml: null,
      rightHeaderHtml: null,
      reset: null,
      selectedItem: []
    }
    config = Object.assign(defaultConfig, config);
    if (!config.leftHeaderHtml) throw new Error('请传入leftHeaderHtml属性');
    if (!config.rightHeaderHtml) throw new Error('请传入rightHeaderHtml属性');
    const originalDataJson = JSON.stringify(config.data);
    this.eq(0).html(`
      <div class="vue-transform-container">
        <div class="vue-transform-item vue-transform-item-left">
          <div class="vue-transform-header" v-html="leftHeaderHtml()">
          </div>
          <div class="vue-transform-content">
            <div class="item">
              <div>
                <input class="yb-checkbox" type="checkbox" v-model="selectAll" />
                <span class="html">全选</span>
              </div>
            </div>
            <div class="wrapper">
              <div class="item" v-for="item of data" :key="item[valueProp]">
                <div>
                  <input v-model="item._value" class="yb-checkbox" type="checkbox" @change="changeValue(item)" />
                  <div class="html" v-html="item[labelProp]"></div>
                  <span class="iconfont" @click="item._showChildren = !item._showChildren"
                        :class="item._showChildren ? 'icon-xiangshang' : 'icon-xiangxia'"></span>
                </div>
                <template v-if="item.children && item.children.length">
                  <div class="children" v-show="item._showChildren">
                    <div class="item" v-for="it of item.children" :key="it[valueProp]">
                      <div>
                        <input v-model="it._value" class="yb-checkbox" type="checkbox" @change="changeValue(it, item)" />
                        <div class="html" v-html="it[labelProp]"></div>
                      </div>
                    </div>
                  </div>
                </template>
              </div>
            </div>
          </div>
        </div>
        <div class="vue-transform-reset" @click="reset">
          <img src="/img/reset.png" width="43" height="43">
          <span>重置</span>
        </div>
        <div class="vue-transform-item vue-transform-item-right">
          <div class="vue-transform-header" v-html="rightHeaderHtml(selectCount())"></div>
          <div class="vue-transform-content">
            <div class="wrapper">
              <div class="item" v-for="(item, prop) of selectedDataObj" :key="prop">
                <div v-html="item[labelProp]"></div>
                <span class="iconfont icon-guanbi" style="font-size: 14px" @click="removeItem(item)"></span>
              </div>
            </div>
          </div>
        </div>
      </div>
    `);
    const vm = new Vue({
      el: this[0],
      data() {
        return {
          data: config.data,
          flatData: [],
          labelProp: config.labelProp || 'label',
          valueProp: config.valueProp || 'guid',
          selectedDataObj: {}
        }
      },
      computed: {
        selectAll: {
          set: function (val) {
            this.data.forEach(item => {
              item._value = val;
              this.selectParent(item);
            })
          },
          get: function () {
            if (!this.data.length) return false;
            return this.flatData.findIndex(item => !item._value) == -1;
          }
        }
      },
      created() {
        this.init();
      },
      methods: {
        leftHeaderHtml: config.leftHeaderHtml,
        rightHeaderHtml: config.rightHeaderHtml,
        selectCount() {
          let count = 0;
          for (let prop in this.selectedDataObj) {
            count ++;
          }
          return count;
        },
        init(flag = true) {
          if (flag) {
            this.selectedDataObj = {}
            config.selectedItem.forEach(item => {
              this.selectedDataObj[item[config.valueProp]] = item;
            });
          }
          this.addProps(this.data, null);
          this.flatDataFn(this.data);
        },
        reset() {
          this.selectedDataObj = {};
          this.data = JSON.parse(originalDataJson);
          this.init();
          config.reset && config.reset();
        },
        // 添加属性
        addProps(data, parent) {
          if (!data || !data.length) return;
          data.forEach(item => {
            this.$set(item, '_showChildren', false);
            this.$set(item, '_value', !!this.selectedDataObj[item[this.valueProp]]);
            if (item._value) {
              this.selectedDataObj[item[this.valueProp]] = item;
            }
            this.addProps(item.children, item);
          })
          if (!parent) return;
          parent._value = parent.children.findIndex(item => !item._value) == -1;
        },
        // 展平数据
        flatDataFn(data) {
          data.forEach(item => {
            if (item.children && item.children.length) {
              this.flatDataFn(item.children);
            } else {
              this.flatData.push(item);
            }
          })
        },
        // 勾选
        changeValue(curr, parent) {
          if (curr.children && curr.children.length) {
            this.selectParent(curr);
          } else {
            this.selectItem(parent, curr);
          }
        },
        // 选择项
        selectItem(item, it) {
          if (it._value) {
            if (this.selectedDataObj[it[this.valueProp]]) return;
            this.selectedDataObj[it[this.valueProp]] = it;
            item._value = item.children.findIndex(i => !i._value) == -1;
          } else {
            const index = this.selectedDataObj[it[this.valueProp]];
            if (!index) return;
            delete this.selectedDataObj[it[this.valueProp]];
            item._value = false;
          }
        },
        // 选择父级
        selectParent(item) {
          if (!item.children) return;
          const value = item._value;
          item.children.forEach(it => {
            it._value = value;
            this.selectItem(item, it);
          })
        },
        // 找父级
        findParents(data, child) {
          return data.find(item => {
            if (!item.children) return false;
            return item.children.findIndex(it => it[this.valueProp] == child[this.valueProp]) != -1;
          }) || {};
        },
        // 移除某项
        removeItem(item) {
          item._value = false;
          this.selectItem(this.findParents(this.data, item), item);
        }
      }
    });
    return {
      // 获取数据
      getData: () => JSON.parse(originalDataJson),
      setData: (data) => {
        vm.data = data;
        vm.init(false);
      },
      setSelectedItem(arr) {
        const obj = {}
        arr.forEach(item => obj[item[config.valueProp]] = item);
        vm.selectedDataObj = obj;
        vm.addProps(vm.data, null);
      },
      getSelectedItem() {
        const res = [];
        for (let prop in vm.selectedDataObj) {
          res.push(clone(vm.selectedDataObj[prop]));
        }
        return res;
      }
    }
  }

  /**
   * 生成guid
   * @param len
   * @param firstU
   * @param radix
   * @returns {string}
   */
  function guid(len = 32, firstU = true, radix = null) {
    let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
    let uuid = [];
    radix = radix || chars.length;

    if (len) {
      // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
      for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
    } else {
      let r;
      // rfc4122标准要求返回的uuid中,某些位为固定的字符
      uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
      uuid[14] = '4';

      for (let i = 0; i < 36; i++) {
        if (!uuid[i]) {
          r = 0 | Math.random() * 16;
          uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
        }
      }
    }
    // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guid不能用作id或者class
    if (firstU) {
      uuid.shift();
      return 'u' + uuid.join('');
    } else {
      return uuid.join('');
    }
  }

  function clone(obj) {
    return JSON.parse(JSON.stringify(obj));
  }

})();